/*
 *  Brno University of Technology
 *
 *  Copyright (C) 2009-2010 by Brno University of Technology and the contributors
 *
 *  Complete list of developers available at our web site:
 *
 *       http://spl.utko.feec.vutbr.cz
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see http://www.gnu.org/licenses/.
 */
package cz.vutbr.feec.utko.ef.core;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.graphviz.GraphViz;

import cz.vutbr.feec.utko.ef.evolution.Chromozome;
import cz.vutbr.feec.utko.ef.evolution.DisplayType;
import cz.vutbr.feec.utko.ef.evolution.stats.DefaultCrossoverAndMutationRateManager;

/**
 * The Class Population contain set of chromozomes.
 */
public class Population implements Iterable<Chromozome> {

	/** The individuals. */
	private Vector<Chromozome> individuals = new Vector<Chromozome>();
	
	private DefaultCrossoverAndMutationRateManager cmRateManager = null;

	private static Logger log = Logger.getLogger(Population.class);
	/**
	 * Adds the.
	 * 
	 * @param in
	 *            the chromozome
	 */
	public void add(Chromozome in) {
		individuals.add(in);
	}

	/**
	 * Adds the.
	 * 
	 * @param p
	 *            the population
	 */
	public void add(Population p) {
		individuals.addAll(p.individuals);
	}

	/**
	 * Cuts the population length. Keeps only first 'length' individuals.
	 * 
	 * @param length
	 *            the length of final population.
	 */
	public void cut(int length) {
		while (individuals.size() > length) {
			individuals.remove(individuals.size() - 1);
		}
	}

	/**
	 * Gets the best individual.
	 * 
	 * @return the best individual
	 */
	public Chromozome getBestIndividual() {
		
		this.sortAccordingToFitness();

		return getIndividual(0);
	}

	/**
	 * Gets the individual.
	 * 
	 * @param i
	 *            the index of an individual
	 * 
	 * @return the individual
	 */
	public Chromozome getIndividual(int i) {
		return individuals.get(i);
	}

	public int getSize() {
		return individuals.size();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	public Iterator<Chromozome> iterator() {
		return individuals.iterator();
	}

	/**
	 * Removes the.
	 * 
	 * @param index
	 *            the index of an individual
	 */
	public void remove(int index) {
		individuals.remove(index);
	}

	/**
	 * Size.
	 * 
	 * @return the size of an individual
	 */
	public int size() {
		return individuals.size();
	}

	/**
	 * Sort according to fitness.
	 */
	public void sortAccordingToFitness() {
		Comparator<Chromozome> cmp = Collections.reverseOrder();
		Collections.sort(individuals, cmp);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		String res = "[";
		for (Chromozome o : individuals) {
			res += o + ";";
		}
		res += "]|";
		return res;
	}

	/**
	 * Sets the population statistic data.
	 * 
	 * @param best the best
	 * @param p the p
	 * @param cfg the cfg
	 */
	public void setPopulationStatisticData(Chromozome best, Population p,Config cfg) {
		GraphViz.TYPE = "svg";
		best.visualize("best.svg", DisplayType.BRIEF);
		GraphViz.TYPE = "gif";

		double fitnessDiversity = getFitnessDiversity(p);
		double meanFitness = getMeanFitness(p);
		
		
	
		log.debug("DIVERSITY " + fitnessDiversity + " MEAN:" + meanFitness
				+ " CROSSOVERRATE:" + cfg.getCrossoverRate() + ", MUTATE_RATE:"
				+ cfg.getMutationRate());
		
	}
	
	/**
	 * Gets the fitness diversity.
	 * 
	 * @param p the p
	 * 
	 * @return the fitness diversity
	 */
	private double getFitnessDiversity(Population p) {
		double mean = 0;
		for (int i = 0; i < p.size(); i++) {
			mean += p.getIndividual(i).getCachedFitness();
		}

		mean = mean / (double) p.size();

		double diversity = 0;
		for (Chromozome t : p) {
			double div = Math.abs((mean - t.getCachedFitness()));
			diversity += div;
		}
		return diversity / (double) p.size();
	}

	/**
	 * Gets the mean fitness.
	 * 
	 * @param p the p
	 * 
	 * @return the mean fitness
	 */
	private double getMeanFitness(Population p) {
		double mean = 0;
		for (int i = 0; i < p.size(); i++) {
			mean += p.getIndividual(i).getCachedFitness();
		}
		return mean / (double) p.size();
	}

	/**
	 * Adds the evolution stats.
	 * 
	 * @param evolutionProcessingTime the evolution processing time
	 * @param cfg the cfg
	 */
	public void addEvolutionStats(long evolutionProcessingTime, Config cfg) {
		cmRateManager = new DefaultCrossoverAndMutationRateManager(cfg);
		evolutionProcessingTime = System.currentTimeMillis()
		- evolutionProcessingTime;
		
		cmRateManager.addEvolutionStats(getCrossoverSuccessRate(),getMutationSuccessRate(), evolutionProcessingTime);
		cfg.setMutationRate(cmRateManager.getMutationRateSuggestion());
		cfg.setCrossoverRate(cmRateManager.getCrossoverRateSuggestion());
		
	}
	
	public double  getCrossoverSuccessRate() {
		return 5;
	}

	public double getMutationSuccessRate() {
		return 5;
	}
}
